/*
 * Created on Mar 8, 2010
 */
package com.mgmaps.gps;

import java.util.TimerTask;
import ro.letsdoitromania.LDIRCanvas;
import com.mgmaps.app.APPSettings;
import com.mgmaps.utils.ExtLogger;

public class GPS {
  GPSReceiver recv;
  private int recvIndex;
  
  LLPoint place;
  private GPSTimerTask task;
  
  private class GPSTimerTask extends TimerTask {
    private boolean firstFix;
    private LLPoint oldPoint;
    private long lastUpdateTime;
    
    public GPSTimerTask() {
      oldPoint = null;
      lastUpdateTime = 0;
      firstFix = true;
    }
    
    public void run() {
      if (APPSettings.gpsEnabled && recv != null)
        try {
          LLPoint p = recv.getPoint();
          if (p != null) {
            long currentTime = System.currentTimeMillis();
            if (currentTime - lastUpdateTime > APPSettings.gpsFixCutoff || !p.equals(oldPoint)) {
              updatePos(p);
              lastUpdateTime = currentTime;
              oldPoint = p;
            }
          }
        }
        catch (Exception ex) {
          ExtLogger.debug("Exception updating position, normal at application startup");
        }
    } // end run
    
    /**
     * Update map position.
     * 
     * @param p new map position (lon/lat)
     */
    protected void updatePos(LLPoint p) {
      place = p;
      LDIRCanvas.instance.notifyGPSUpdate(firstFix);
      firstFix = false;
    }
  } // end timer task
  
  
  public GPS() {
    // GPS receiver
    recvIndex = -1;
    recv = null;
    setReceiver(false);
    
    // run the GPS timer task every second
    task = new GPSTimerTask();
    APPSettings.timer.schedule(task, 1000, 1000);
  }
  
  public boolean setReceiver(boolean force) {
    final int newIndex = getGPSReceiverIndex(APPSettings.gpsDevice);
    
    // nothing to do?
    if (newIndex == recvIndex && !force)
      return false;
    
    try {
      // deactivate
      if (recvIndex >= 0) {
        recv.deactivate();
        recv = null;
        recvIndex = -1;
      }
      
      // check that the receiver is supported and > 0, return false if not
      if (newIndex < 0)
        return false;
      
      // create receiver
      GPSReceiver newRecv = createReceiver(newIndex);
      if (newRecv == null)
        return false;
    
      recvIndex = newIndex;
      recv = newRecv;
      recv.activate();
      if (task != null)
        task.run();
      return true;
    }
    catch (Exception ex) {
      ExtLogger.handle(ex, false, "Error setting active GPS receiver");
      return false;
    }
  }
  
  /**
   * Get current receiver.
   * @return recv
   */
  public GPSReceiver getReceiver() {
    return recv;
  }
  
  /**
   * Instantiate a GPSReceiver.
   * @param num receiver number
   * @return GPSReceiver object
   */
  private GPSReceiver createReceiver(int num) {
    GPSReceiver r = null;
    
    switch(num) {
      case 0:
        try {
          r = (GPSReceiver) Class.forName("com.mgmaps.gps.GPSInternal").newInstance();
        }
        catch (Exception ex) {
          ExtLogger.handle(ex, false, "Error instantiating GPSInternal");
          r = null;
        }
        break;
        
      case 1:
        try {
          r = (GPSReceiver) Class.forName("com.mgmaps.gps.GPSBluetooth").newInstance();
        }
        catch (Exception ex) {
          ExtLogger.handle(ex, false, "Error instantiating GPSBluetooth");
          r = null;
        }
        break;
    }
    
    return r;
  }
  
  /**
   * Get GPS receiver index given gpsDevice name.
   * @param name gpsDevice
   * @return 0 (internal), 1 (bluetooth), 2 (serial) -- or -1 if other 
   */
  public static int getGPSReceiverIndex(String name) {
    if (name == null)
      return -1;
    else if (APPSettings.hasLAPI && name.equals("Internal"))
      return 0;
    else if (APPSettings.hasBT && name.startsWith("btspp://"))
      return 1;
    else
      return -1;
  }
  
  /**
   * Called when the application is about to exit, stops the threads.
   */
  public void notifyExit() {
    if (recv != null)
      recv.notifyExit();
  }
  
  /**
   * Get the current GPS place object.
   * @return the GPSPlace object.
   */
  public LLPoint getPlace() {
    return place;
  }
  
}
